package com.itheima.protocol.distributedLock;

import java.util.List;
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;

public class DistributedLock {

    private static final String ROOT_LOCKS = "/locks";   //根节点
    private ZooKeeper zooKeeper;
    private int sessionTimeOut;     //超时时间
    private String lockId;          //记录锁节点ID

    private CountDownLatch countDownLatch = new CountDownLatch(1);

    private static final byte[] data = {1, 2};   //节点数据

    public DistributedLock() throws Exception {
        this.zooKeeper = ZookeeperClient.getInstance();
        this.sessionTimeOut = ZookeeperClient.getSESSIONTIMEOUT();
    }

    //获取锁的方法(创建临时有序节点)
    public boolean lock() {

        try {
            lockId = zooKeeper.create(ROOT_LOCKS + "/", data
                    , ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.println(Thread.currentThread().getName() + "--> 成功创建Lock节点：{" + lockId + "},开始竞争锁");

            //获取根节点下的所有子节点
            List<String> childrenNodes = zooKeeper.getChildren(ROOT_LOCKS, true);

            //排序，从小到大
            SortedSet<String> sortedSet = new TreeSet<>();
            for (String node : childrenNodes) {
                sortedSet.add(ROOT_LOCKS + "/" + node);
            }

            /**
             * 获取最小的节点
             * 对于读请求
             * 如果没有比自己序号小的节点，或者所有比自己序号小的子节点都是读请求
             * 那么表明自己已经成功获取到共享锁，同时开始执行读取的逻辑
             *
             * 如果比自己序号小的子节点中有写请求，那么就需要进入等待
             * 对于写请求
             * 如果自己不是序号最小的子节点，那么就需要进入等待
             */
            String first = sortedSet.first();
            if(lockId.equals(first)){
                //如果相同，表示我以成功获取锁
                System.out.println(Thread.currentThread().getName() + "--> 成功获取锁,lock 节点为：{" + lockId + "}");

                return true;
            }
            SortedSet<String> lessThanLockId = sortedSet.headSet(lockId);
            if(!lessThanLockId.isEmpty()){
                //拿到比当前lockID更小的上一个节点
                String prevLockId = lessThanLockId.last();
                //监控比我小的上一个节点
                zooKeeper.exists(prevLockId, new LockWatcher(countDownLatch));
                countDownLatch.await(sessionTimeOut,TimeUnit.MILLISECONDS);
                System.out.println(Thread.currentThread().getName() + "成功释放锁【"+ lockId +"】");
            }

            return true;

        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return false;
    }

    public boolean unLock() {
        System.out.println(Thread.currentThread().getName() + "开始释放锁【"+ lockId +"】");
        try {
            zooKeeper.delete(lockId, -1);
            System.out.println("节点["+lockId+"]被删除");
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(10);
        Random random = new Random();
        for (int i = 0; i < 10; i++) {

            new Thread(()->{
                DistributedLock lock = null;

                try {
                    lock = new DistributedLock();
                    latch.countDown();
                    latch.await();
                    lock.lock();
                    Thread.sleep(random.nextInt(500));
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    if(lock != null) {
                        lock.unLock();
                    }
                }
            }).start();
        }
    }
}
